using System;
using System.Net;
using System.Net.Sockets;

namespace SharpMud.Net
{
    public class NetworkConnection : INetworkConnection
    {
        #region Private Variables
        private System.IO.Stream _Stream;
        private System.Net.Sockets.Socket _Socket;
        private System.IO.StreamReader _In;
        private System.IO.StreamWriter _Out;
        private System.Net.IPEndPoint _LocalEndpoint;
        private System.Net.IPEndPoint _RemoteEndpoint;

        private ConnectionStatus _Status;
        private System.DateTime _ConnectTime;
        #endregion

        #region Constructors
        private NetworkConnection()
        {
        }

        protected NetworkConnection(System.Net.Sockets.Socket socket, System.IO.Stream stream)
        {
            _LocalEndpoint = (System.Net.IPEndPoint)socket.LocalEndPoint;
            _RemoteEndpoint = (System.Net.IPEndPoint)socket.RemoteEndPoint;

            _Socket = socket;
            _Stream = stream;

            _In = new System.IO.StreamReader(_Stream);
            _Out = new System.IO.StreamWriter(_Stream);

            _Status = ConnectionStatus.AttemptingConnect;

            this.LinkLost += new ConnectionEventHandler(NetworkConnection_LinkLost);
            this.Connected += new ConnectionEventHandler(NetworkConnection_Connected);
        }

        public NetworkConnection(System.Net.Sockets.Socket socket)
            : this(socket, new System.Net.Sockets.NetworkStream(socket, System.IO.FileAccess.ReadWrite))
        {
        }
        #endregion

        #region Public Properties
        public System.IO.StreamReader In
        {
            get
            {
                return _In;
            }
        }

        public System.IO.StreamWriter Out
        {
            get
            {
                return _Out;
            }
        }
        #endregion

        #region INetworkConnection Members

        public System.IO.Stream Stream
        {
            get
            {
                return this._Stream;
            }
        }

        public void Accept()
        {
            if (this._Connected != null)
            {
                if (_Connected != null)
                {
                    _Connected((INetworkConnection)this, new ConnectionEventArgs((IPEndPoint)_Socket.LocalEndPoint, (IPEndPoint)_Socket.RemoteEndPoint));
                }
            }
        }

        public ConnectionStatus Status
        {
            get
            {
                return this._Status;
            }
        }

        public void Close()
        {
            this._Status = SharpMud.Net.ConnectionStatus.Closing;

            if (this._Closing != null)
                this._Closing(this, new ConnectionEventArgs(this.LocalEndpoint, this.RemoteEndpoint));

            this._Socket.Close();

            this._Status = SharpMud.Net.ConnectionStatus.Disconnected;

            if (this._Disconnected != null)
                this._Disconnected(this, new ConnectionEventArgs(this.LocalEndpoint, this.RemoteEndpoint));
        }

        public void Pulse()
        {
            // TODO:  Add NetworkConnection.Pulse implementation
        }

        public System.Net.IPEndPoint LocalEndpoint
        {
            get
            {
                return this._LocalEndpoint;
            }
        }

        public System.Net.IPEndPoint RemoteEndpoint
        {
            get
            {
                return this._RemoteEndpoint;
            }
        }

        private event ConnectionEventHandler _LinkLost;
        public event ConnectionEventHandler LinkLost
        {
            add
            {
                _LinkLost += value;
            }
            remove
            {
                _LinkLost -= value;
            }
        }

        private event ConnectionEventHandler _Closing;
        public event ConnectionEventHandler Closing
        {
            add
            {
                _Closing += value;
            }
            remove
            {
                _Closing -= value;
            }
        }

        private event ConnectionEventHandler _Connected;
        public event ConnectionEventHandler Connected
        {
            add
            {
                _Connected += value;
            }
            remove
            {
                _Connected -= value;
            }
        }

        private event ConnectionEventHandler _Disconnected;
        public event ConnectionEventHandler Disconnected
        {
            add
            {
                _Disconnected += value;
            }
            remove
            {
                _Disconnected -= value;
            }
        }

        public DateTime ConnectTime
        {
            get
            {
                return _ConnectTime;
            }
        }

        #endregion

        private void NetworkConnection_LinkLost(INetworkConnection sender, ConnectionEventArgs e)
        {
            this.Close();
        }

        private void NetworkConnection_Connected(INetworkConnection sender, ConnectionEventArgs e)
        {
            this._Status = ConnectionStatus.Connected;
            this._ConnectTime = System.DateTime.Now;
            this._LocalEndpoint = e.LocalEndpoint;
            this._RemoteEndpoint = e.RemoteEndpoint;
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (this._Socket != null)
            {
                if(_Stream!=null)
                {
                    try
                    {
                        _Stream.Close();
                    }   catch (System.ObjectDisposedException e)    {}

                    try
                    {
                        _Stream.Dispose();
                    }   catch (System.ObjectDisposedException e)    {}
                    _Stream = null;
                }

                if (_Socket != null)
                {
                    try
                    {
                        _Socket.Close();
                    }
                    catch (System.Net.Sockets.SocketException e)    {}
                    catch (System.ObjectDisposedException e)        {}
                    _Socket = null;
                }
            }
        }

        #endregion
    }
}
